function getNumberFromPx(px: string | number | null | undefined): number {
  if (!px) {
    return 0
  }
  if (typeof px === "number") {
    return px
  }
  return Number(px.replace("px", ""))
}

function addPxToNumber(number: number, round: boolean = true): string {
  number = round ? Math.round(number) : number
  return `${number}px`
}

function HexToHSV(color: HashString): { h: number; s: number; v: number } {
  const [r, g, b] = color
    .replace("#", "")
    .match(/.{1,2}/g)
    ?.map((x) => parseInt(x, 16)) || [0, 0, 0]

  const max = Math.max(r, g, b)
  const min = Math.min(r, g, b)
  const v = max / 255
  const d = max - min
  const s = max === 0 ? 0 : d / max
  const h =
    max === min
      ? 0
      : max === r
      ? (g - b) / d + (g < b ? 6 : 0)
      : max === g
      ? (b - r) / d + 2
      : (r - g) / d + 4
  return { h: h * 60, s, v }
}

function HSVToHex(h: number, s: number, v: number): HashString {
  s /= 100
  v /= 100
  h /= 360

  let r = 0,
    g = 0,
    b = 0

  let i = Math.floor(h * 6)
  let f = h * 6 - i
  let p = v * (1 - s)
  let q = v * (1 - f * s)
  let t = v * (1 - (1 - f) * s)

  switch (i % 6) {
    case 0:
      ;(r = v), (g = t), (b = p)
      break
    case 1:
      ;(r = q), (g = v), (b = p)
      break
    case 2:
      ;(r = p), (g = v), (b = t)
      break
    case 3:
      ;(r = p), (g = q), (b = v)
      break
    case 4:
      ;(r = t), (g = p), (b = v)
      break
    case 5:
      ;(r = v), (g = p), (b = q)
      break
  }
  r = Math.round(r * 255)
  g = Math.round(g * 255)
  b = Math.round(b * 255)
  return `#${[r, g, b].map((x) => x.toString(16).padStart(2, "0")).join("")}`
}

function getRandomColor() {
  return HSVToHex(Math.random() * 360, 25, 100)
}

async function confirm(message: string): Promise<boolean> {
  return new Promise((resolve) => {
    const confirmed = window.confirm(message)
    resolve(confirmed)
  })
}

function getTextContent(html: string | null) {
  if (!html || !isHTMLString(html)) {
    return html || ""
  }
  const tmp = document.createElement("div")
  tmp.innerHTML = html || ""
  const textContent = tmp.textContent || tmp.innerText || ""
  tmp.remove()
  return textContent
}

function RGBToHex(rgb: RGBString): HashString {
  const [r, g, b] = rgb
    .replace("rgb(", "")
    .replace(")", "")
    .split(",")
    .map((x) => parseInt(x))
  return `#${[r, g, b].map((x) => x.toString(16).padStart(2, "0")).join("")}`
}

function getRGB(color: HashString | RGBString | string | null): HashString {
  if (!color) {
    return "#ffffff"
  }
  if (color.startsWith("rgb")) {
    return RGBToHex(color as RGBString)
  } else if (!color.startsWith("#") && color.match(/\b[a-fA-F0-9]{3,6}\b/g)) {
    return `#${color}` as HashString
  }
  return color as HashString
}

function isHTMLString(str: string) {
  return /<[a-z][\s\S]*>/i.test(str)
}

function copyToClipboard(
  text: string | object,
  e: ClipboardEvent,
  copyFormat = "text/plain"
) {
  if (typeof text !== "string") {
    text = JSON.stringify(text)
  }
  e.clipboardData?.setData(copyFormat, text)
}

function stripExtension(string: string) {
  const lastDotPosition = string.lastIndexOf(".")
  if (lastDotPosition === -1) {
    return string
  }
  return string.substr(0, lastDotPosition)
}

function findNearestSiblingIndex(e: MouseEvent) {
  let nearestElementIndex = 0
  let minDistance = Number.MAX_VALUE
  let parent = e.target as HTMLElement

  const elements = Array.from(parent.children)
  elements.forEach(function (element, index) {
    const rect = element.getBoundingClientRect()
    const centerX = rect.left + rect.width / 2
    const centerY = rect.top + rect.height / 2

    const distance = Math.sqrt(
      Math.pow(centerX - e.clientX, 2) + Math.pow(centerY - e.clientY, 2)
    )
    if (distance < minDistance) {
      minDistance = distance
      nearestElementIndex = index
      const positionBitmask = element.compareDocumentPosition(e.target as Node)
      // sourcery skip: possible-incorrect-bitwise-operator
      if (positionBitmask & Node.DOCUMENT_POSITION_PRECEDING) {
        // before
      } else {
        nearestElementIndex += 1
      }
    }
  })
  return nearestElementIndex
}

// converts border-color to borderColor
function kebabToCamelCase(str: string) {
  return str.replace(/-([a-z])/g, function (g) {
    return g[1].toUpperCase()
  })
}

function isJSONString(str: string) {
  try {
    JSON.parse(str)
  } catch (e) {
    return false
  }
  return true
}

function isTargetEditable(e: Event) {
  const target = e.target as HTMLElement
  const isEditable = target.isContentEditable
  const isInput = target.tagName === "INPUT" || target.tagName === "TEXTAREA"
  return isEditable || isInput
}
function getDataForKey(datum: Object, key: string) {
  const data = Object.assign({}, datum)
  return key.split(".").reduce((d, key) => (d ? d[key] : null), data) as string
}

export {
  HSVToHex,
  HexToHSV,
  RGBToHex,
  addPxToNumber,
  confirm,
  copyToClipboard,
  findNearestSiblingIndex,
  getDataForKey,
  getNumberFromPx,
  getRGB,
  getRandomColor,
  getTextContent,
  isHTMLString,
  isJSONString,
  isTargetEditable,
  kebabToCamelCase,
  stripExtension,
}
